package cn.iocoder.yudao.module.trade.service.order;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.converter.WxMappingJackson2HttpMessageConverter;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.member.api.address.MemberAddressApi;
import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.api.shipping.WeChatApi;
import cn.iocoder.yudao.module.pay.api.shipping.dto.*;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.api.wechat_card.WeChatCardApi;
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.*;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemOtherDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemOtherMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderLogMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryModelEnum;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import cn.iocoder.yudao.module.trade.enums.giftcard.GiftCardStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.*;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog;
import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils;
import cn.iocoder.yudao.module.trade.service.cart.CartService;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import cn.iocoder.yudao.module.trade.service.giftcard.GiftCardService;
import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
import cn.iocoder.yudao.module.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO;
import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler;
import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.http.*;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.minusTime;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;

/**
 * 交易订单【写】Service 实现类
 *
 * @author LeeYan9
 * @since 2022-08-26
 */
@Service
@Slf4j
public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {

    @Resource
    private TradeOrderMapper tradeOrderMapper;
    @Resource
    private TradeOrderItemMapper tradeOrderItemMapper;

    @Resource
    private TradeOrderItemOtherMapper tradeOrderItemOtherMapper;
    @Resource
    private TradeNoRedisDAO tradeNoRedisDAO;

    @Resource
    private List<TradeOrderHandler> tradeOrderHandlers;

    @Resource
    private CartService cartService;
    @Resource
    private TradePriceService tradePriceService;
    @Resource
    private DeliveryExpressService deliveryExpressService;
    @Resource
    private TradeMessageService tradeMessageService;

    @Resource
    private PayOrderApi payOrderApi;
    @Resource
    private MemberAddressApi addressApi;
    @Resource
    private ProductCommentApi productCommentApi;

    @Resource
    private TradeOrderProperties tradeOrderProperties;

    @Resource
    private WeChatApi weChatApi;

    @Resource
    private SocialUserApi socialUserApi;

    @Resource
    private TradeOrderLogMapper tradeOrderLogMapper;

    @Resource
    private GiftCardService giftCardService;

    // =================== Order ===================

    @Resource
    WeChatCardApi weChatCardApi;

    @Resource
    RestTemplate restTemplate;

    @Resource
    ObjectMapper objectMapper;

    @Override
    public AppTradeOrderSettlementRespVO settlementOrder(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
        // 1. 获得收货地址
        MemberAddressRespDTO address = getAddress(userId, settlementReqVO.getAddressId());
        if (address != null) {
            settlementReqVO.setAddressId(address.getId());
        }

        // 2. 计算价格
        TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, settlementReqVO);

        // 3. 拼接返回
        return TradeOrderConvert.INSTANCE.convert(calculateRespBO, address);
    }

    /**
     * 获得用户地址
     *
     * @param userId    用户编号
     * @param addressId 地址编号
     * @return 地址
     */
    private MemberAddressRespDTO getAddress(Long userId, Long addressId) {
        if (addressId != null) {
            return addressApi.getAddress(addressId, userId);
        }
        return addressApi.getDefaultAddress(userId);
    }

    /**
     * 计算订单价格
     *
     * @param userId          用户编号
     * @param settlementReqVO 结算信息
     * @return 订单价格
     */
    private TradePriceCalculateRespBO calculatePrice(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
        // 1. 如果来自购物车，则获得购物车的商品
        List<CartDO> cartList = cartService.getCartList(userId,
                convertSet(settlementReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId));

        // 2. 计算价格
        TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList);
        calculateReqBO.getItems().forEach(item -> Assert.isTrue(item.getSelected(), // 防御性编程，保证都是选中的
                "商品({}) 未设置为选中", item.getSkuId()));
        return tradePriceService.calculatePrice(calculateReqBO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE)
    public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO, Integer terminal) {
        // 1.1 价格计算
        TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
        // 1.2 构建订单
        TradeOrderDO order = buildTradeOrder(userId, userIp, createReqVO, calculateRespBO, terminal);
        List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO);

        // 2. 订单创建前的逻辑
        tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));

        // 3. 保存订单
        tradeOrderMapper.insert(order);
        orderItems.forEach(orderItem -> orderItem.setOrderId(order.getId()));
        tradeOrderItemMapper.insertBatch(orderItems);
        // 4. 订单创建后的逻辑
        afterCreateTradeOrder(order, orderItems, createReqVO);
        return order;
    }

    /**
     * 【礼品卡】创建交易订单
     *
     * @param userId        登录用户
     * @param userIp        用户 IP 地址
     * @param createReqVO   创建交易订单请求模型
     * @param terminal      终端 {@link cn.iocoder.yudao.framework.common.enums.TerminalEnum}
     * @param giftCardPrice 礼品卡价格
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE)
    public TradeOrderDO createOrderGiftCard(Long userId, String userIp, AppTradeOrderGiftCardCreateReqVO createReqVO, Integer terminal, Integer giftCardPrice) {
        // 1.1 价格计算
        TradePriceCalculateReqBO calculateReqBO = new TradePriceCalculateReqBO();
        calculateReqBO.setUserId(userId);
        calculateReqBO.setPointStatus(false);
        calculateReqBO.setDeliveryType(createReqVO.getDeliveryType());
        List<AppTradeOrderGiftCardCreateReqVO.Item> items = createReqVO.getItems();
        List<TradePriceCalculateReqBO.Item> itemList = new ArrayList<>();
        items.forEach(x -> {
            TradePriceCalculateReqBO.Item i = new TradePriceCalculateReqBO.Item();
            BeanUtils.copyProperties(x, i);
            i.setSelected(true);
            itemList.add(i);
        });
        calculateReqBO.setItems(itemList);
        TradePriceCalculateRespBO calculateRespBO = tradePriceService.calculatePriceGiftCard(calculateReqBO);

        // 1.2 构建订单 - 礼品卡
        TradeOrderDO order = buildTradeOrderGiftCard(userId, userIp, createReqVO, calculateRespBO, terminal);

        //礼品卡价格
        order.setTotalPrice(giftCardPrice * 100);
        order.setPayPrice(giftCardPrice * 100);

        List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO);

        // 2. 订单创建前的逻辑
        tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));

        order.setType(TradeOrderTypeEnum.GiftCard.getType());
        //礼品卡订单使用状态 1
//        order.setGiftUseStatus(1);

        // 3. 保存订单
        tradeOrderMapper.insert(order);
        orderItems.forEach(orderItem -> orderItem.setOrderId(order.getId()));
        tradeOrderItemMapper.insertBatch(orderItems);

        // 4. 订单创建后的逻辑
        afterCreateTradeOrderGiftCard(order, orderItems, createReqVO, giftCardPrice);
        return order;
    }

    private TradeOrderDO buildTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO,
                                         TradePriceCalculateRespBO calculateRespBO, Integer terminal) {
        TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO);
        order.setType(calculateRespBO.getType());
        order.setNo(tradeNoRedisDAO.generate(TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX));
        order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
        order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
        order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
        order.setTerminal(terminal);
        // 支付 + 退款信息
        order.setAdjustPrice(0).setPayStatus(false);
        order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
        // 物流信息
        order.setDeliveryType(createReqVO.getDeliveryType());
        if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType())) {
            MemberAddressRespDTO address = addressApi.getAddress(createReqVO.getAddressId(), userId);
            Assert.notNull(address, "地址({}) 不能为空", createReqVO.getAddressId()); // 价格计算时，已经计算
            order.setReceiverName(address.getName()).setReceiverMobile(address.getMobile())
                    .setReceiverAreaId(address.getAreaId()).setReceiverDetailAddress(address.getDetailAddress());
        } else if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.PICK_UP.getType())) {
            order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile());
            order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // 随机一个核销码，长度为 8 位
        }
        return order;
    }

    private Integer calculateRespBOPriceDiscountPrice(TradePriceCalculateRespBO tradePriceCalculateRespBO) {
        if (tradePriceCalculateRespBO == null) {
            return null;
        }
        TradePriceCalculateRespBO.Price price = tradePriceCalculateRespBO.getPrice();
        if (price == null) {
            return null;
        }
        Integer discountPrice = price.getDiscountPrice();
        if (discountPrice == null) {
            return null;
        }
        return discountPrice;
    }

    /**
     * 构建订单 - 礼品卡
     */
    private TradeOrderDO buildTradeOrderGiftCard(Long userId, String clientIp, AppTradeOrderGiftCardCreateReqVO createReqVO,
                                                 TradePriceCalculateRespBO calculateRespBO, Integer terminal) {
        AppTradeOrderCreateReqVO createReqVO1 = new AppTradeOrderCreateReqVO();
        BeanUtils.copyProperties(createReqVO, createReqVO1);
        TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO1, calculateRespBO);

        order.setType(calculateRespBO.getType());
        order.setNo(tradeNoRedisDAO.generate(TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX));
        order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
        order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
        order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
        order.setTerminal(terminal);
        // 支付 + 退款信息
        order.setAdjustPrice(0).setPayStatus(false);
        order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
        // 物流信息
        order.setDeliveryType(createReqVO.getDeliveryType());
        if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType())) {
            // TODO: 2024/1/18 礼品卡在使用的时候才配置地址
//            MemberAddressRespDTO address = addressApi.getAddress(createReqVO.getAddressId(), userId);
//            Assert.notNull(address, "地址({}) 不能为空", createReqVO.getAddressId()); // 价格计算时，已经计算
            order.setReceiverName("暂无").setReceiverMobile("暂无")
                    .setReceiverAreaId(0).setReceiverDetailAddress("暂无");
        } else if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.PICK_UP.getType())) {
            order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile());
            order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // 随机一个核销码，长度为 8 位
        }
        return order;
    }

    private List<TradeOrderItemDO> buildTradeOrderItems(TradeOrderDO tradeOrderDO,
                                                        TradePriceCalculateRespBO calculateRespBO) {
        return TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO);
    }

    /**
     * 订单创建后，执行后置逻辑
     * <p>
     * 例如说：优惠劵的扣减、积分的扣减、支付单的创建等等
     *
     * @param order       订单
     * @param orderItems  订单项
     * @param createReqVO 创建订单请求
     */
    private void afterCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
                                       AppTradeOrderCreateReqVO createReqVO) {
        // 1. 执行订单创建后置处理器
        tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems));

        // 2. 删除购物车商品
        Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId);
        if (CollUtil.isNotEmpty(cartIds)) {
            cartService.deleteCart(order.getUserId(), cartIds);
        }

        // 3. 生成预支付
        createPayOrder(order, orderItems);

        // 4. 插入订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus());

        // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
    }

    /**
     * 礼品卡 订单创建后，执行后置逻辑
     *
     * @param order         订单
     * @param orderItems    订单项
     * @param createReqVO   创建订单请求
     * @param giftCardPrice 礼品卡价格
     */
    private void afterCreateTradeOrderGiftCard(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
                                               AppTradeOrderGiftCardCreateReqVO createReqVO, Integer giftCardPrice) {
        // 1. 执行订单创建后置处理器
        tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems));
        // 2. 生成预支付
        createPayOrderGiftCard(order, orderItems, giftCardPrice);
        // 3. 插入订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus());
    }

    private void createPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
        // 创建支付单，用于后续的支付
        PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
                order, orderItems, tradeOrderProperties);
        Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO);

        // 更新到交易单上
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setPayOrderId(payOrderId));
        order.setPayOrderId(payOrderId);
    }

    private void createPayOrderGiftCard(TradeOrderDO order, List<TradeOrderItemDO> orderItems, Integer giftCardPrice) {
        // 创建支付单，用于后续的支付
        PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
                order, orderItems, tradeOrderProperties);
        Long payOrderId = payOrderApi.createOrderGiftCard(payOrderCreateReqDTO, giftCardPrice);

        // 更新到交易单上
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setPayOrderId(payOrderId));
        order.setPayOrderId(payOrderId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_PAY)
    public void updateOrderPaid(Long id, Long payOrderId) {
        // 1. 校验并获得交易订单（可支付）
        KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
        TradeOrderDO order = orderResult.getKey();
        PayOrderRespDTO payOrder = orderResult.getValue();

        // 2. 更新 TradeOrderDO 状态为已支付，等待发货
        int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
                new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayStatus(true)
                        .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
        if (updateCount == 0) {
            throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
        }

        // 2.1 如果 TradeOrderDO type 为礼品卡订单 更新订单关联的礼品卡状态
        if (TradeOrderTypeEnum.GiftCard.getType().equals(order.getType())) {
            giftCardService.updateByTradeOrderId(GiftCardStatusEnum.CAN_USE.getType(), order.getId(), 0);
        }

        // 3. 执行 TradeOrderHandler 的后置处理
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
        tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems));

        // 4. 记录订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
        TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue());
    }

    /**
     * 校验交易订单满足被支付的条件
     * <p>
     * 1. 交易订单未支付
     * 2. 支付单已支付
     *
     * @param id         交易订单编号
     * @param payOrderId 支付订单编号
     * @return 交易订单
     */
    private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPayable(Long id, Long payOrderId) {
        // 校验订单是否存在
        TradeOrderDO order = validateOrderExists(id);
        // 校验订单未支付
        if (!TradeOrderStatusEnum.isUnpaid(order.getStatus()) || order.getPayStatus()) {
            log.error("[validateOrderPaid][order({}) 不处于待支付状态，请进行处理！order 数据是：{}]",
                    id, JsonUtils.toJsonString(order));
            throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
        }
        // 校验支付订单匹配
        if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
            log.error("[validateOrderPaid][order({}) 支付单不匹配({})，请进行处理！order 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(order));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
        }

        // 校验支付单是否存在
        PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
        if (payOrder == null) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 不存在，请进行处理！]", id, payOrderId);
            throw exception(ORDER_NOT_FOUND);
        }
        // 校验支付单已支付
        if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 未支付，请进行处理！payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
        }
        // 校验支付金额一致
        if (ObjectUtil.notEqual(payOrder.getPrice(), order.getPayPrice())) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配，请进行处理！order 数据是：{}，payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
        }
        // 校验支付订单匹配（二次）
        if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
            log.error("[validateOrderPaid][order({}) 支付单不匹配({})，请进行处理！payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
        }
        return new KeyValue<>(order, payOrder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_DELIVERY)
    public void deliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO) {
        // 1.1 校验并获得交易订单（可发货）
        TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
        // 1.2 校验 deliveryType 是否为快递，是快递才可以发货
        if (ObjectUtil.notEqual(order.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType())) {
            throw exception(ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS);
        }
        //校验是否是待发货状态
        if (order.getStatus() != TradeOrderStatusEnum.UNDELIVERED.getStatus()) {
            throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
        }
        // 2. 更新订单为已发货
        TradeOrderDO updateOrderObj = new TradeOrderDO();
        // 2.1 快递发货
        DeliveryExpressDO express = null;
        if (ObjectUtil.notEqual(deliveryReqVO.getLogisticsId(), TradeOrderDO.LOGISTICS_ID_NULL)) {
            express = deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId());
            updateOrderObj.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo());
        } else {
            // 2.2 无需发货
            updateOrderObj.setLogisticsId(0L).setLogisticsNo("");
        }
        // 执行更新
        updateOrderObj.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now());
        int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), updateOrderObj);
        if (updateCount == 0) {
            throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
        }
        // 3. 记录订单日志
        insertTradeOrderExpressLog(deliveryReqVO.getLogisticsId(), deliveryReqVO.getLogisticsNo(), order.getId(), order.getStatus());
        // 4. 发送站内信
        tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO()
                .setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null));


        //  更新订单发货类型和发货模式
        TradeOrderDO tradeOrderUp1 = tradeOrderMapper.selectById(order.getId());
        tradeOrderUp1.setDeliveryType(deliveryReqVO.getDeliveryType());
        tradeOrderUp1.setDeliveryMode(deliveryReqVO.getDeliveryMode());
        tradeOrderMapper.updateById(tradeOrderUp1);

        //5.调用微信小程序发货信息录入接口

        //<editor-fold desc="调用微信小程序发货信息录入接口">
        //是否调用微信发货接口   当拆分发货时，且未全部发货不调用
        boolean upShippingInfoBool = true;

        WxDeliverGoodsDTO vo = new WxDeliverGoodsDTO();
        OrderKeyDTO order_key = new OrderKeyDTO();
        order_key.setMchid("");
        order_key.setOrder_number_type(1);    // 必填
        PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
        order_key.setOut_trade_no(payOrder.getNo());
        order_key.setTransaction_id("");
        List<ShippingDTO> list = new ArrayList<>();

        //统一发货
        if (deliveryReqVO.getDeliveryMode() == DeliveryModelEnum.UNIFICATION.getType()) {
            vo.setDelivery_mode(1);        // 必填
            vo.setIs_all_delivered(false);    // 分拆发货模式时必填
            ShippingDTO shipping = new ShippingDTO();
            //物流发货
            if (deliveryReqVO.getDeliveryType() == DeliveryTypeEnum.EXPRESS.getType()) {
                //物流模式，发货方式枚举值：1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品，虚拟商品，例如话费充值，点卡等，无实体配送形式 4、用户自提
                //我们目前只有  1和2
                vo.setLogistics_type(1);    // 必填
                for (TradeOrderPageItemRespVO.Item item : deliveryReqVO.getItems()) {
                    TradeOrderItemDO tradeOrderItemUp1 = tradeOrderItemMapper.selectById(item.getId());
                    tradeOrderItemUp1.setLogisticsId(deliveryReqVO.getLogisticsId());
                    tradeOrderItemUp1.setLogisticsNo(deliveryReqVO.getLogisticsNo());
                    tradeOrderItemMapper.updateById(tradeOrderItemUp1);
                }
                //物流单号，物流快递发货时必填
                shipping.setTracking_no(deliveryReqVO.getLogisticsNo());
                //查询快递公司编号
                DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
                shipping.setExpress_company(deliveryExpress.getCode());
            } else if (deliveryReqVO.getDeliveryType() == DeliveryTypeEnum.INTRA_CITY_SERVICE.getType()) {//同城发货
                vo.setLogistics_type(2);    // 必填
            }
            List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
            String spuNames = orderItems.stream().map(p -> p.getSpuName()).collect(Collectors.joining(";"));
            shipping.setItem_desc(spuNames);    // 必填
            ContactDTO contact = new ContactDTO();
            //contact.setConsignor_contact();    // 手机号掩码处理
            if (StringUtils.isNotBlank(order.getReceiverMobile())) {
                contact.setReceiver_contact(order.getReceiverMobile().replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
            }
            shipping.setContact(contact);
            list.add(shipping);
        } else if (deliveryReqVO.getDeliveryMode() == DeliveryModelEnum.split.getType()) {
            vo.setDelivery_mode(2);        // 必填
            vo.setIs_all_delivered(true);    // 分拆发货模式时必填
            //物流模式，发货方式枚举值：1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品，虚拟商品，例如话费充值，点卡等，无实体配送形式 4、用户自提
            //我们目前只有  1和2
            vo.setLogistics_type(1);    // 必填
            //按产品名称和规格型号 分拆发货
            for (TradeOrderPageItemRespVO.Item item : deliveryReqVO.getItems()) {
                //过滤掉 已经退款的数据
                if (Objects.equals(item.getAfterSaleStatus(),TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {
                    continue;
                }
                //如果物流单号为空，说明没全部发完，则不调用微信发货接口
                if (item.getLogisticsId() == null || item.getLogisticsNo().isBlank()) {
                    upShippingInfoBool = false;
                    continue;
                }
                TradeOrderItemDO tradeOrderItemUp1 = tradeOrderItemMapper.selectById(item.getId());
                tradeOrderItemUp1.setLogisticsId(item.getLogisticsId());
                tradeOrderItemUp1.setLogisticsNo(item.getLogisticsNo());
                tradeOrderItemMapper.updateById(tradeOrderItemUp1);
                //记录订单快递日志
                if (!StringUtils.equals(item.getLogisticsNo(), deliveryReqVO.getLogisticsNo())) {
                    insertTradeOrderExpressLog(item.getLogisticsId(), item.getLogisticsNo(), order.getId(), order.getStatus());
                }
                if (deliveryReqVO.getDeliveryType() == DeliveryTypeEnum.EXPRESS.getType()) {
                    ShippingDTO shipping = setShipping(item.getLogisticsNo(), item.getLogisticsId(), item.getProperties(), item.getSpuName(), order.getReceiverMobile());
                    list.add(shipping);
                }
            }
        } else if (deliveryReqVO.getDeliveryMode() == DeliveryModelEnum.split_qty.getType()) {
            vo.setDelivery_mode(2);        // 必填
            vo.setIs_all_delivered(true);    // 分拆发货模式时必填
            //物流模式，发货方式枚举值：1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品，虚拟商品，例如话费充值，点卡等，无实体配送形式 4、用户自提
            //我们目前只有  1和2
            vo.setLogistics_type(1);    // 必填
            //分拆发货
            for (TradeOrderPageItemRespVO.Item item : deliveryReqVO.getItemsOther()) {
                //过滤掉 已经退款的数据
                if (Objects.equals(item.getAfterSaleStatus(),TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {
                    continue;
                }
                TradeOrderItemDO tradeOrderItemUp1 = tradeOrderItemMapper.selectById(item.getId());
                //数量分拆发货  存储到trade_order_item_other表
                TradeOrderItemOtherDO otherDO = TradeOrderConvert.INSTANCE.convertOther(tradeOrderItemUp1);
                otherDO.setId(null);
                otherDO.setLogisticsId(item.getLogisticsId());
                otherDO.setLogisticsNo(item.getLogisticsNo());
                tradeOrderItemOtherMapper.insert(otherDO);
                //如果物流单号为空，说明没全部发完，则不调用微信发货接口
                if (item.getLogisticsId() == null || item.getLogisticsNo().isBlank()) {
                    upShippingInfoBool = false;
                    continue;
                }
                //记录订单快递日志
                if (!StringUtils.equals(item.getLogisticsNo(), deliveryReqVO.getLogisticsNo())) {
                    insertTradeOrderExpressLog(item.getLogisticsId(), item.getLogisticsNo(), order.getId(), order.getStatus());
                }
                if (deliveryReqVO.getDeliveryType() == DeliveryTypeEnum.EXPRESS.getType()) {
                    ShippingDTO shipping = setShipping(item.getLogisticsNo(), item.getLogisticsId(), item.getProperties(), item.getSpuName(), order.getReceiverMobile());
                    list.add(shipping);
                }
            }
        }

        //------------混合发货(前端传过来的发货类型目前都是快递发货)，部分商品发快递，部分商品发同城(快递名称叫同城快递code =TCPS，需要过滤)，
        // 因为微信发货接口要么是快递，要么是同城，所以如果是混合发货推微信接口的时候走快递发货
        //过滤掉是同城发货的商品，并把同城发货的商品拼接到某一个快递发货商品上。 ----------------
        if (deliveryReqVO.getDeliveryType() == DeliveryTypeEnum.EXPRESS.getType()) {
            Map<String, List<ShippingDTO>> listMap = shippingConvert(list);
            List<ShippingDTO> kdfhList = listMap.get("kdfh");
            if (!CollectionUtils.isEmpty(kdfhList)) {
                list = kdfhList;
            } else {
                //如果没有快递物流，就改成走  同城发货的方式
                vo.setLogistics_type(2);
                list = listMap.get("tcps");
            }
        }
        PayerDTO payer = new PayerDTO();
        //获取openId
        SocialUserRespDTO socialUserByMemberUserId = socialUserApi.getSocialUserByMemberUserId(order.getUserId());
        payer.setOpenid(socialUserByMemberUserId.getOpenid());    // 必填
        vo.setOrder_key(order_key);    // 必填
        //微信小程序只最多传10个
        if (list.size() > 10) {
            list = list.subList(0, 10);
        }
        vo.setShipping_list(list);    // 必填
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        vo.setUpload_time(sdf.format(new Date()));    // 必填
        vo.setPayer(payer);    // 必填

        //全部发完快递，且不是钱包余额支付 才掉微信发货接口
        if (upShippingInfoBool && !StringUtils.equals(order.getPayChannelCode(), PayChannelEnum.WALLET.getCode())) {
            if (!weChatApi.uploadShippingInfo(vo)) {
                throw exception(ORDER_DELIVERY_FAIL_WECHAT);
            }
        }
        //</editor-fold>
    }

    /**
     * 按商品规格型号分拆发货
     *
     * @param deliveryReqVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_DELIVERY)
    public void splitDeliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO) {
        TradeOrderItemDO tradeOrderItemDO = tradeOrderItemMapper.selectById(deliveryReqVO.getId());
        if (tradeOrderItemDO == null) {
            throw exception(ORDER_ITEM_NOT_FOUND);
        }
        TradeOrderDO order = tradeOrderMapper.selectById(tradeOrderItemDO.getOrderId());
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        //校验是否是已发货状态
        if (order.getStatus() != TradeOrderStatusEnum.DELIVERED.getStatus()) {
            throw exception(ORDER_DELIVERY_DELIVERED);
        }
        if (StringUtils.isNotBlank(tradeOrderItemDO.getLogisticsNo())) {
            throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
        }
        // 3. 记录订单日志
        insertTradeOrderExpressLog(deliveryReqVO.getLogisticsId(), deliveryReqVO.getLogisticsNo(), order.getId(), order.getStatus());
        // 更新快递单号
        tradeOrderItemDO.setLogisticsNo(deliveryReqVO.getLogisticsNo());
        tradeOrderItemDO.setLogisticsId(deliveryReqVO.getLogisticsId());
        tradeOrderItemMapper.updateById(tradeOrderItemDO);

        //5.调用微信小程序发货信息录入接口

        //<editor-fold desc="调用微信小程序发货信息录入接口">
        List<TradeOrderItemDO> tradeOrderItemList = tradeOrderItemMapper.selectListByOrderId(order.getId());
        WxDeliverGoodsDTO vo = new WxDeliverGoodsDTO();
        OrderKeyDTO order_key = new OrderKeyDTO();
        order_key.setMchid("");
        order_key.setOrder_number_type(1);    // 必填
        PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
        order_key.setOut_trade_no(payOrder.getNo());
        order_key.setTransaction_id("");
        List<ShippingDTO> list = new ArrayList<>();
        //------------分拆发货
        if (order.getDeliveryMode() == DeliveryModelEnum.split.getType()) {
            vo.setDelivery_mode(2);        // 必填
            vo.setIs_all_delivered(true);    // 分拆发货模式时必填
            //物流模式，发货方式枚举值：1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品，虚拟商品，例如话费充值，点卡等，无实体配送形式 4、用户自提
            //我们目前只有  1和2
            vo.setLogistics_type(1);    // 必填
            //分拆发货
            for (TradeOrderItemDO item : tradeOrderItemList) {
                //过滤掉 已经退款的数据
                if (Objects.equals(item.getAfterSaleStatus(),TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {
                    continue;
                }
                //如果物流单号为空，说明没全部发完，则不调用微信发货接口
                if (item.getLogisticsId() == null || item.getLogisticsNo().isBlank()) {
                    return;
                }
                ShippingDTO shipping = setShipping(item.getLogisticsNo(), item.getLogisticsId(),
                        TradeOrderConvert.INSTANCE.convertProductPropertyList(item.getProperties()), item.getSpuName(), order.getReceiverMobile());
                list.add(shipping);
            }

            Map<String, List<ShippingDTO>> listMap = shippingConvert(list);
            List<ShippingDTO> kdfhList = listMap.get("kdfh");
            if (!CollectionUtils.isEmpty(kdfhList)) {
                list = kdfhList;
            } else {
                //如果没有快递物流，就改成走  同城发货的方式
                vo.setLogistics_type(2);
                list = listMap.get("tcps");
            }
            PayerDTO payer = new PayerDTO();
            //获取openId
            SocialUserRespDTO socialUserByMemberUserId = socialUserApi.getSocialUserByMemberUserId(order.getUserId());
            payer.setOpenid(socialUserByMemberUserId.getOpenid());    // 必填
            vo.setOrder_key(order_key);    // 必填
            //微信小程序只最多传10个
            if (list.size() > 10) {
                list = list.subList(0, 10);
            }
            vo.setShipping_list(list);    // 必填
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
            vo.setUpload_time(sdf.format(new Date()));    // 必填
            vo.setPayer(payer);    // 必填
            //是否调用微信发货接口   未全部发货时不调用,非微信支付不调用
            if (!StringUtils.equals(order.getPayChannelCode(), PayChannelEnum.WALLET.getCode())) {
                if (!weChatApi.uploadShippingInfo(vo)) {
                    throw exception(ORDER_DELIVERY_FAIL_WECHAT);
                }
            }

        }
        //</editor-fold>
    }

    /**
     * 按数量分拆发货
     *
     * @param deliveryReqVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_DELIVERY)
    public void splitQtyDeliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO) {
        TradeOrderItemOtherDO otherDO = tradeOrderItemOtherMapper.selectById(deliveryReqVO.getId());
        if (otherDO == null) {
            throw exception(ORDER_ITEM_NOT_FOUND);
        }
        TradeOrderDO order = tradeOrderMapper.selectById(otherDO.getOrderId());
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        //校验是否是已发货状态
        if (order.getStatus() != TradeOrderStatusEnum.DELIVERED.getStatus()) {
            throw exception(ORDER_DELIVERY_DELIVERED);
        }
        if (StringUtils.isNotBlank(otherDO.getLogisticsNo())) {
            throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
        }

        // 3. 记录订单日志
        insertTradeOrderExpressLog(deliveryReqVO.getLogisticsId(), deliveryReqVO.getLogisticsNo(), order.getId(), order.getStatus());

        otherDO.setLogisticsId(deliveryReqVO.getLogisticsId());
        otherDO.setLogisticsNo(deliveryReqVO.getLogisticsNo());
        tradeOrderItemOtherMapper.updateById(otherDO);

        //5.调用微信小程序发货信息录入接口

        //<editor-fold desc="调用微信小程序发货信息录入接口">
        List<TradeOrderItemOtherDO> tradeOrderItemOtherList = tradeOrderItemOtherMapper.selectList(
                new LambdaQueryWrapper<TradeOrderItemOtherDO>().eq(TradeOrderItemOtherDO::getOrderId, order.getId()));
        WxDeliverGoodsDTO vo = new WxDeliverGoodsDTO();
        OrderKeyDTO order_key = new OrderKeyDTO();
        order_key.setMchid("");
        order_key.setOrder_number_type(1);    // 必填
        PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
        order_key.setOut_trade_no(payOrder.getNo());
        order_key.setTransaction_id("");
        List<ShippingDTO> list = new ArrayList<>();
        //------------分拆发货
        if (order.getDeliveryMode() == DeliveryModelEnum.split_qty.getType()) {
            vo.setDelivery_mode(2);        // 必填
            vo.setIs_all_delivered(true);    // 分拆发货模式时必填
            //物流模式，发货方式枚举值：1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品，虚拟商品，例如话费充值，点卡等，无实体配送形式 4、用户自提
            //我们目前只有  1和2
            vo.setLogistics_type(1);    // 必填
            //分拆发货
            for (TradeOrderItemOtherDO item : tradeOrderItemOtherList) {
                //过滤掉 已经退款的数据
                if (Objects.equals(item.getAfterSaleStatus(),TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {
                    continue;
                }
                //如果物流单号为空，说明没全部发完，则不调用微信发货接口
                if (item.getLogisticsId() == null || item.getLogisticsNo().isBlank()) {
                    return;
                }
                ShippingDTO shipping = setShipping(item.getLogisticsNo(), item.getLogisticsId(),
                        TradeOrderConvert.INSTANCE.convertProductPropertyOtherList(item.getProperties()), item.getSpuName(), order.getReceiverMobile());
                list.add(shipping);
            }

            Map<String, List<ShippingDTO>> listMap = shippingConvert(list);
            List<ShippingDTO> kdfhList = listMap.get("kdfh");
            if (!CollectionUtils.isEmpty(kdfhList)) {
                list = kdfhList;
            } else {
                //如果没有快递物流，就改成走  同城发货的方式
                vo.setLogistics_type(2);
                list = listMap.get("tcps");
            }
            PayerDTO payer = new PayerDTO();
            //获取openId
            SocialUserRespDTO socialUserByMemberUserId = socialUserApi.getSocialUserByMemberUserId(order.getUserId());
            payer.setOpenid(socialUserByMemberUserId.getOpenid());    // 必填
            vo.setOrder_key(order_key);    // 必填
            //微信小程序只最多传10个
            if (list.size() > 10) {
                list = list.subList(0, 10);
            }
            vo.setShipping_list(list);    // 必填
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
            vo.setUpload_time(sdf.format(new Date()));    // 必填
            vo.setPayer(payer);    // 必填
            //是否调用微信发货接口   未全部发货时不调用,非微信支付不调用
            if (!StringUtils.equals(order.getPayChannelCode(), PayChannelEnum.WALLET.getCode())) {
                if (!weChatApi.uploadShippingInfo(vo)) {
                    throw exception(ORDER_DELIVERY_FAIL_WECHAT);
                }
            }

        }

        //</editor-fold>
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String sendImportExcel(List<TradeOrderSendImportExcelVO> list1) {
        if (CollectionUtils.isEmpty(list1)) {
            return "导入数据不能为空";
        }
        List<TradeOrderSendImportVO> list = TradeOrderConvert.INSTANCE.convertExcelList(list1);
        for (TradeOrderSendImportVO sendImportExcelVO : list) {
            if (StringUtils.isBlank(sendImportExcelVO.getNo())) {
                return "订单号不能为空";
            }
            if (StringUtils.isBlank(sendImportExcelVO.getSpuName())) {
                return "商品名称不能为空";
            }
            if (StringUtils.isBlank(sendImportExcelVO.getSpecifications())) {
                return "规格型号不能为空";
            }
            if (StringUtils.isBlank(sendImportExcelVO.getLogisticsName())) {
                return "发货物流公司不能为空";
            }
            if (StringUtils.isBlank(sendImportExcelVO.getLogisticsNo())) {
                return "发货物流单号不能为空";
            }
            TradeOrderDO tradeOrderDO = tradeOrderMapper.selectOne(new LambdaQueryWrapper<TradeOrderDO>()
                    .eq(TradeOrderDO::getNo, sendImportExcelVO.getNo()));
            if (tradeOrderDO == null) {
                return sendImportExcelVO.getNo() + "订单未查询到";
            }
            sendImportExcelVO.setTradeOrderDO(tradeOrderDO);

            if (tradeOrderDO.getStatus() != TradeOrderStatusEnum.UNDELIVERED.getStatus() &&
                    tradeOrderDO.getStatus() != TradeOrderStatusEnum.DELIVERED.getStatus()) {
                return sendImportExcelVO.getNo() + "订单状态非待发货状态";
            }
            TradeOrderItemDO tradeOrderItemDO = null;
            List<TradeOrderItemDO> tradeOrderItemDOList = tradeOrderItemMapper.selectList(new LambdaQueryWrapper<TradeOrderItemDO>()
                    .eq(TradeOrderItemDO::getOrderId, tradeOrderDO.getId())
                    .eq(TradeOrderItemDO::getSpuName, sendImportExcelVO.getSpuName()));
            for (TradeOrderItemDO orderItemDO : tradeOrderItemDOList) {
                long count = orderItemDO.getProperties().stream().filter(p -> StringUtils.equals(p.getValueName(), sendImportExcelVO.getSpecifications())).count();
                if (count >= 0) {
                    tradeOrderItemDO = orderItemDO;
                    break;
                }
            }
            if (tradeOrderItemDO == null) {
                return sendImportExcelVO.getNo() + "_" + sendImportExcelVO.getSpuName() + "订单明细未查询到";
            }
            sendImportExcelVO.setTradeOrderItemDO(tradeOrderItemDO);

            if (tradeOrderItemDO.getLogisticsId() != null) {
                return sendImportExcelVO.getNo() + "_" + sendImportExcelVO.getSpuName() + "订单状态非待发货状态";
            }

            List<DeliveryExpressDO> deliveryExpressList = deliveryExpressService.getDeliveryExpressListByName(sendImportExcelVO.getLogisticsName());
            if (CollectionUtils.isEmpty(deliveryExpressList)) {
                return sendImportExcelVO.getLogisticsName() + ",发货物流公司未找到,请联系管理员添加";
            }
            DeliveryExpressDO deliveryExpressDO = deliveryExpressList.get(0);
            sendImportExcelVO.setDeliveryExpressDO(deliveryExpressDO);
        }
        Map<String, Long> countMap = list.stream()
                .map(p -> p.getNo() + "_" + p.getSpuName() + "_" + p.getSpecifications()) // 将订单号和商品名称和规格合并作为新的唯一标识
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        Set<String> duplicates = countMap.entrySet().stream()
                .filter(entry -> entry.getValue() > 1) // 过滤出元素数量大于1的分组，即存在重复数据
                .map(Map.Entry::getKey) // 将分组唯一标识转换为Set集合
                .collect(Collectors.toSet());
        if (!duplicates.isEmpty()) {
            return "存在重复数据：" + duplicates.stream().findFirst().get();
        }
        Map<String, List<TradeOrderSendImportVO>> groupExcelMap = list.stream().collect(Collectors.groupingBy(v -> v.getNo()));
        for (String key : groupExcelMap.keySet()) {
            List<TradeOrderSendImportVO> excelList = groupExcelMap.get(key);
            int size = excelList.size();
            TradeOrderSendImportVO tradeOrderSendImportVO = excelList.get(0);
            //订单
            TradeOrderDO tradeOrderDO = tradeOrderSendImportVO.getTradeOrderDO();
            //查询物流订单是否一样
            boolean allLogisticsNoSame = excelList.stream().map(p -> p.getLogisticsNo()).collect(Collectors.toSet()).size() == 1;
            List<TradeOrderItemDO> tradeOrderItemList = tradeOrderItemMapper.selectList(new LambdaQueryWrapper<TradeOrderItemDO>()
                    .eq(TradeOrderItemDO::getOrderId, tradeOrderSendImportVO.getTradeOrderDO().getId()));
            if (tradeOrderDO.getStatus() == TradeOrderStatusEnum.UNDELIVERED.getStatus()) {
                TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO();
                deliveryReqVO.setId(tradeOrderDO.getId());
                deliveryReqVO.setLogisticsId(tradeOrderSendImportVO.getDeliveryExpressDO().getId());
                deliveryReqVO.setLogisticsNo(tradeOrderSendImportVO.getLogisticsNo());
                //只有一条记录且订单状态是未发货 就走统一发货
                //如果多条记录 且订单状态是未发货 且物流订单都一样，就走统一发货
                //其他情况走 分拆发货
                if ((size == 1 && tradeOrderItemList.size() == 1) || (size > 1 && allLogisticsNoSame && tradeOrderItemList.size() == size)) {
                    deliveryReqVO.setDeliveryMode(1);
                } else {
                    deliveryReqVO.setDeliveryMode(2);
                }
                deliveryReqVO.setDeliveryType(1);
                List<TradeOrderPageItemRespVO.Item> items = new ArrayList<>();
                for (TradeOrderSendImportVO sendImportVO : excelList) {
                    TradeOrderPageItemRespVO.Item item = new TradeOrderPageItemRespVO.Item();
                    item.setId(sendImportVO.getTradeOrderItemDO().getId());
                    item.setOrderId(sendImportVO.getTradeOrderDO().getId());
                    item.setSpuName(sendImportVO.getTradeOrderItemDO().getSpuName());
                    item.setLogisticsId(sendImportVO.getDeliveryExpressDO().getId());
                    item.setLogisticsNo(sendImportVO.getLogisticsNo());
                    items.add(item);
                }
                deliveryReqVO.setItems(items);
                deliveryOrder(deliveryReqVO);
            } else if (tradeOrderDO.getStatus() == TradeOrderStatusEnum.DELIVERED.getStatus()) {
                for (TradeOrderSendImportVO sendImportVO : excelList) {
                    TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO();
                    deliveryReqVO.setId(sendImportVO.getTradeOrderItemDO().getId());
                    deliveryReqVO.setLogisticsId(sendImportVO.getDeliveryExpressDO().getId());
                    deliveryReqVO.setLogisticsNo(sendImportVO.getLogisticsNo());
                    splitDeliveryOrder(deliveryReqVO);
                }
            }
        }
        return "success";
    }

    /**
     * 校验交易订单满足被发货的条件
     * <p>
     * 1. 交易订单未发货
     *
     * @param id 交易订单编号
     * @return 交易订单
     */
    private TradeOrderDO validateOrderDeliverable(Long id) {
        TradeOrderDO order = validateOrderExists(id);
        // 1. 校验订单是否未发货
        if (ObjectUtil.equal(TradeOrderRefundStatusEnum.ALL.getStatus(), order.getRefundStatus())) {
            throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE);
        }

        // 2. 执行 TradeOrderHandler 前置处理
        tradeOrderHandlers.forEach(handler -> handler.beforeDeliveryOrder(order));
        return order;
    }

    @NotNull
    private TradeOrderDO validateOrderExists(Long id) {
        // 校验订单是否存在
        TradeOrderDO order = tradeOrderMapper.selectById(id);
        if (order == null) {
            order = tradeOrderMapper.selectGiftOrderById(id);
            if (order == null) {
                throw exception(ORDER_NOT_FOUND);
            }
        }
        return order;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_RECEIVE)
    public void receiveOrderByMember(Long userId, Long id) {
        // 校验并获得交易订单（可收货）
        TradeOrderDO order = validateOrderReceivable(userId, id);

        // 收货订单
        receiveOrder0(order);
    }

    @Override
    public int receiveOrderBySystem() {
        // 1. 查询过期的待支付订单
        LocalDateTime expireTime = minusTime(tradeOrderProperties.getReceiveExpireTime());
        List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndDeliveryTimeLt(
                TradeOrderStatusEnum.DELIVERED.getStatus(), expireTime);
        if (CollUtil.isEmpty(orders)) {
            return 0;
        }

        // 2. 遍历执行，逐个取消
        int count = 0;
        for (TradeOrderDO order : orders) {
            try {
                getSelf().receiveOrderBySystem(order);
                count++;
            } catch (Throwable e) {
                log.error("[receiveOrderBySystem][order({}) 自动收货订单异常]", order.getId(), e);
            }
        }
        return count;
    }

    /**
     * 自动收货单个订单
     *
     * @param order 订单
     */
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_RECEIVE)
    public void receiveOrderBySystem(TradeOrderDO order) {
        receiveOrder0(order);
    }

    /**
     * 收货订单的核心实现
     *
     * @param order 订单
     */
    private void receiveOrder0(TradeOrderDO order) {
        // 更新 TradeOrderDO 状态为已完成
        int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
                new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(LocalDateTime.now()));
        if (updateCount == 0) {
            throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
        }

        // 插入订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
    }

    /**
     * 校验交易订单满足可售货的条件
     * <p>
     * 1. 交易订单待收货
     *
     * @param userId 用户编号
     * @param id     交易订单编号
     * @return 交易订单
     */
    private TradeOrderDO validateOrderReceivable(Long userId, Long id) {
        // 校验订单是否存在
        TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 校验订单是否是待收货状态
        if (!TradeOrderStatusEnum.isDelivered(order.getStatus())) {
            throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
        }
        return order;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
    public void cancelOrderByMember(Long userId, Long id) {
        // 1.1 校验存在
        TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 1.2 校验状态
        if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
            throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
        }

        // 2. 取消订单
        cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
    }

    @Override
    public int cancelOrderBySystem() {
        // 1. 查询过期的待支付订单
        LocalDateTime expireTime = minusTime(tradeOrderProperties.getPayExpireTime());
        List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndCreateTimeLt(
                TradeOrderStatusEnum.UNPAID.getStatus(), expireTime);
        if (CollUtil.isEmpty(orders)) {
            return 0;
        }

        // 2. 遍历执行，逐个取消
        int count = 0;
        for (TradeOrderDO order : orders) {
            try {
                getSelf().cancelOrderBySystem(order);
                count++;
            } catch (Throwable e) {
                log.error("[cancelOrderBySystem][order({}) 过期订单异常]", order.getId(), e);
            }
        }
        return count;
    }

    /**
     * 自动取消单个订单
     *
     * @param order 订单
     */
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
    public void cancelOrderBySystem(TradeOrderDO order) {
        cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
    }

    /**
     * 取消订单的核心实现
     *
     * @param order      订单
     * @param cancelType 取消类型
     */
    private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
        // 1. 更新 TradeOrderDO 状态为已取消
        int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
                new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
                        .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now()));
        if (updateCount == 0) {
            throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
        }

        // 2. 执行 TradeOrderHandler 的后置处理
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
        tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems));

        // 3. 增加订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
    }

    /**
     * 如果金额全部被退款，则取消订单
     * 如果还有未被退款的金额，则无需取消订单
     *
     * @param order       订单
     * @param refundPrice 退款金额
     */
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_CANCEL_AFTER_SALE)
    public void cancelOrderByAfterSale(TradeOrderDO order, Integer refundPrice) {
        // 1. 更新订单
        if (refundPrice < order.getPayPrice()) {
            return;
        }
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
                .setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
                .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));

        // 2. 执行 TradeOrderHandler 的后置处理
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
        tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE)
    public void deleteOrder(Long userId, Long id) {
        // 1.1 校验存在
        TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 1.2 校验状态
        if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus())) {
            throw exception(ORDER_DELETE_FAIL_STATUS_NOT_CANCEL);
        }
        // 2. 删除订单
        tradeOrderMapper.deleteById(id);

        // 3. 记录日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
    }

    @Override
    public void updateOrderRemark(TradeOrderRemarkReqVO reqVO) {
        // 校验并获得交易订单
        validateOrderExists(reqVO.getId());

        // 更新
        TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(reqVO);
        tradeOrderMapper.updateById(order);
    }

    @Override
    public void updateOrderInvoice(TradeOrderInvoiceReqVO reqVO) {
        // 校验并获得交易订单
        validateOrderExists(reqVO.getId());

        // 更新
        TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(reqVO);
        tradeOrderMapper.updateById(order);
    }

    @Override
    public void updateTradeOrderDO(TradeOrderDO tradeOrderDO) {
        tradeOrderMapper.updateById(tradeOrderDO);
    }

    @Override
    public void updateTradeOrderGiftDO(TradeOrderDO tradeOrderDO) {
        tradeOrderMapper.updateById(tradeOrderDO);
        tradeOrderMapper.setGiftUseStatusNull(tradeOrderDO.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_PRICE)
    public void updateOrderPrice(TradeOrderUpdatePriceReqVO reqVO) {
        // 1.1 校验交易订单
        TradeOrderDO order = validateOrderExists(reqVO.getId());
        if (order.getPayStatus()) {
            throw exception(ORDER_UPDATE_PRICE_FAIL_PAID);
        }
        // 1.2 校验调价金额是否变化
        if (order.getAdjustPrice() > 0) {
            throw exception(ORDER_UPDATE_PRICE_FAIL_ALREADY);
        }
        // 1.3 支付价格不能为 0
        int newPayPrice = order.getPayPrice() + order.getAdjustPrice();
        if (newPayPrice <= 0) {
            throw exception(ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR);
        }

        // 2. 更新订单
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
                .setAdjustPrice(reqVO.getAdjustPrice()).setPayPrice(newPayPrice));

        // 3. 更新 TradeOrderItem，需要做 adjustPrice 的分摊
        List<TradeOrderItemDO> orderOrderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
        List<Integer> dividePrices = TradePriceCalculatorHelper.dividePrice2(orderOrderItems, newPayPrice);
        List<TradeOrderItemDO> updateItems = new ArrayList<>();
        for (int i = 0; i < orderOrderItems.size(); i++) {
            TradeOrderItemDO item = orderOrderItems.get(i);
            updateItems.add(new TradeOrderItemDO().setId(item.getId()).setAdjustPrice(dividePrices.get(i))
                    .setPayPrice(item.getPayPrice() + dividePrices.get(i)));
        }
        tradeOrderItemMapper.updateBatch(updateItems);

        // 4. 更新支付订单
        payOrderApi.updatePayOrderPrice(order.getPayOrderId(), newPayPrice);

        // 5. 记录订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus(),
                MapUtil.<String, Object>builder().put("oldPayPrice", MoneyUtils.fenToYuanStr(order.getPayPrice()))
                        .put("newPayPrice", MoneyUtils.fenToYuanStr(newPayPrice)).build());
    }

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_ADDRESS)
    public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) {
        // 校验交易订单
        TradeOrderDO order = validateOrderExists(reqVO.getId());
        // 只有待发货状态，才可以修改订单收货地址；
        if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())) {
            throw exception(ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED);
        }

        // 更新
        tradeOrderMapper.updateById(TradeOrderConvert.INSTANCE.convert(reqVO));

        // 记录订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
    }

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE)
    public void pickUpOrderByAdmin(Long id) {
        getSelf().pickUpOrder(tradeOrderMapper.selectById(id));
    }

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE)
    public void pickUpOrderByAdmin(String pickUpVerifyCode) {
        getSelf().pickUpOrder(tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode));
    }

    @Override
    public TradeOrderDO getByPickUpVerifyCode(String pickUpVerifyCode) {
        return tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode);
    }

    @Transactional(rollbackFor = Exception.class)
    public void pickUpOrder(TradeOrderDO order) {
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        if (ObjUtil.notEqual(DeliveryTypeEnum.PICK_UP.getType(), order.getDeliveryType())) {
            throw exception(ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP);
        }
        receiveOrder0(order);
    }

    // =================== Order Item ===================

    @Override
    public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) {
        // 更新订单项
        updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
                TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), afterSaleId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
        // 1.1 更新订单项
        updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
                TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null);
        // 1.2 执行 TradeOrderHandler 的后置处理
        TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
        TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
        tradeOrderHandlers.forEach(handler -> handler.afterCancelOrderItem(order, orderItem));

        // 2.1 更新订单的退款金额、积分
        Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
        Integer orderRefundPoint = (order.getRefundPoint() == null ? 0 : order.getRefundPoint()) + orderItem.getUsePoint();
        Integer refundStatus = isAllOrderItemAfterSaleSuccess(order.getId()) ?
                TradeOrderRefundStatusEnum.ALL.getStatus() // 如果都售后成功，则需要取消订单
                : TradeOrderRefundStatusEnum.PART.getStatus();
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
                .setRefundStatus(refundStatus)
                .setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint));
        // 2.2 如果全部退款，则进行取消订单
        getSelf().cancelOrderByAfterSale(order, orderRefundPrice);
    }

    @Override
    public void updateOrderItemWhenAfterSaleCancel(Long id) {
        // 更新订单项
        updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
                TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
    }

    private void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
                                                Long afterSaleId) {
        // 更新订单项
        int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId);
        if (updateCount <= 0) {
            throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
        }

    }

    /**
     * 判断指定订单的所有订单项，是不是都售后成功
     *
     * @param id 订单编号
     * @return 是否都售后成功
     */
    private boolean isAllOrderItemAfterSaleSuccess(Long id) {
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
        return orderItems.stream().allMatch(orderItem -> Objects.equals(orderItem.getAfterSaleStatus(),
                TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_COMMENT)
    public Long createOrderItemCommentByMember(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO) {
        // 1.1 先通过订单项 ID，查询订单项是否存在
        TradeOrderItemDO orderItem = tradeOrderItemMapper.selectByIdAndUserId(createReqVO.getOrderItemId(), userId);
        if (orderItem == null) {
            throw exception(ORDER_ITEM_NOT_FOUND);
        }
        // 1.2 校验订单相关状态
        TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderItem.getOrderId(), userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) {
            throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED);
        }


        if (order.getCommentStatus() != null && order.getCommentStatus()) {
            throw exception(ORDER_COMMENT_STATUS_NOT_FALSE);
        }

        // 2. 创建评价
        Long commentId = createOrderItemComment0(orderItem, createReqVO);

        // 3. 如果订单项都评论了，则更新订单评价状态
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
        if (!anyMatch(orderItems, item -> Objects.equals(item.getCommentStatus(), Boolean.FALSE))) {
            tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE)
                    .setFinishTime(LocalDateTime.now()));
            // 增加订单日志。注意：只有在所有订单项都评价后，才会增加
            TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
        }
        return commentId;
    }

    @Override
    public int createOrderItemCommentBySystem() {
        // 1. 查询过期的待支付订单
        LocalDateTime expireTime = minusTime(tradeOrderProperties.getCommentExpireTime());
        List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndReceiveTimeLt(
                TradeOrderStatusEnum.COMPLETED.getStatus(), expireTime, false);
        if (CollUtil.isEmpty(orders)) {
            return 0;
        }

        // 2. 遍历执行，逐个取消
        int count = 0;
        for (TradeOrderDO order : orders) {
            try {
                getSelf().createOrderItemCommentBySystemBySystem(order);
                count++;
            } catch (Throwable e) {
                log.error("[createOrderItemCommentBySystem][order({}) 过期订单异常]", order.getId(), e);
            }
        }
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId) {
        tradeOrderMapper.updateById(
                new TradeOrderDO().setId(orderId).setCombinationActivityId(activityId)
                        .setCombinationRecordId(combinationRecordId).setCombinationHeadId(headId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelPaidOrder(Long userId, Long orderId) {
        // TODO 芋艿：这里实现要优化下；
        TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
    }

    /**
     * 创建单个订单的评论
     *
     * @param order 订单
     */
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_COMMENT)
    public void createOrderItemCommentBySystemBySystem(TradeOrderDO order) {
        // 1. 查询未评论的订单项
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderIdAndCommentStatus(
                order.getId(), Boolean.FALSE);
        if (CollUtil.isEmpty(orderItems)) {
            return;
        }

        // 2. 逐个评论
        for (TradeOrderItemDO orderItem : orderItems) {
            // 2.1 创建评价
            AppTradeOrderItemCommentCreateReqVO commentCreateReqVO = new AppTradeOrderItemCommentCreateReqVO()
                    .setOrderItemId(orderItem.getId()).setAnonymous(false).setContent("")
                    .setBenefitScores(5).setDescriptionScores(5);
            createOrderItemComment0(orderItem, commentCreateReqVO);

            // 2.2 更新订单项评价状态
            tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
        }

        // 3. 所有订单项都评论了，则更新订单评价状态
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE)
                .setFinishTime(LocalDateTime.now()));
        // 增加订单日志。注意：只有在所有订单项都评价后，才会增加
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
    }

    /**
     * 创建订单项的评论的核心实现
     *
     * @param orderItem   订单项
     * @param createReqVO 评论内容
     * @return 评论编号
     */
    private Long createOrderItemComment0(TradeOrderItemDO orderItem, AppTradeOrderItemCommentCreateReqVO createReqVO) {
        // 1. 创建评价
        ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItem);
        Long commentId = productCommentApi.createComment(productCommentCreateReqDTO);

        // 2. 更新订单项评价状态
        tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
        return commentId;
    }

    // =================== 营销相关的操作 ===================

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private TradeOrderUpdateServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }


    /**
     * 构造ShippingDTO
     *
     * @param logisticsNo
     * @param logisticsId
     * @param properties
     * @param spuName
     * @param receiverMobile
     * @return
     */
    private ShippingDTO setShipping(String logisticsNo, Long logisticsId, List<ProductPropertyValueDetailRespVO> properties,
                                    String spuName, String receiverMobile) {
        ShippingDTO shipping = new ShippingDTO();
        //物流单号，物流快递发货时必填
        shipping.setTracking_no(logisticsNo);
        //查询快递公司编号
        DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(logisticsId);
        shipping.setExpress_company(deliveryExpress.getCode());

        String specificationStr = "";
        if (!CollectionUtils.isEmpty(properties)) {
            specificationStr = properties.get(0).getValueName();
        }
        shipping.setItem_desc(spuName + specificationStr);    // 必填
        ContactDTO contact = new ContactDTO();
        if (StringUtils.isNotBlank(receiverMobile)) {
            contact.setReceiver_contact(receiverMobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
        }
        shipping.setContact(contact);
        return shipping;
    }

    private void insertTradeOrderExpressLog(Long logisticsId, String logisticsNo, Long orderId, Integer orderStatus) {
        DeliveryExpressDO express = deliveryExpressService.validateDeliveryExpress(logisticsId);
        TradeOrderLogDO tradeOrderLogDO = new TradeOrderLogDO();
        tradeOrderLogDO.setUserId(WebFrameworkUtils.getLoginUserId());
        tradeOrderLogDO.setUserType(2);
        tradeOrderLogDO.setOrderId(orderId);
        tradeOrderLogDO.setBeforeStatus(orderStatus);
        tradeOrderLogDO.setAfterStatus(TradeOrderStatusEnum.DELIVERED.getStatus());
        tradeOrderLogDO.setOperateType(TradeOrderOperateTypeEnum.ADMIN_DELIVERY.getType());
        tradeOrderLogDO.setContent("已发货，快递公司：" + (express != null ? express.getName() : "") + "，快递单号：" + logisticsNo);
        tradeOrderLogMapper.insert(tradeOrderLogDO);
    }

    @Override
    public void updateUserIdByTradeOrderId(Long userId, Long id) {
        //改变TradeOrder user_id
        tradeOrderMapper.updateUserIdByTradeOrderId(userId, id);
        //改变TradeOrderItem user_id
        tradeOrderItemMapper.updateTradeOrderItemUserIdByOrderId(userId, id);
    }

    @Override
    public Long updateOrderCompleted(Long orderId) {
        return tradeOrderMapper.updateOrderCompleted(TradeOrderStatusEnum.COMPLETED.getStatus(), orderId);
    }

    @Override
    public Long updateOrderDelivered(Long orderId) {
        return tradeOrderMapper.updateOrderCompleted(TradeOrderStatusEnum.DELIVERED.getStatus(), orderId);
    }


    @Override
    public String wayBillToken(WaybillTokenReqVO vo) {

        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(vo.getOrderId());
        if(ObjectUtil.isNull(tradeOrderDO)){
            throw exception(ORDER_NOT_FOUND);
        }
        PayOrderRespDTO payOrder = payOrderApi.getOrder(tradeOrderDO.getPayOrderId());
        if(ObjectUtil.isNull(payOrder)){
            throw exception(ORDER_NOT_FOUND);
        }

        List<TradeOrderItemDO> tradeOrderItemDOS =
                tradeOrderItemMapper.selectListByOrderId(Long.valueOf(vo.getOrderId()));

        String accessToken = weChatCardApi.getAccessToken();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        WaybillTokenTransferVO waybillTokenTransferVO = new WaybillTokenTransferVO();
        waybillTokenTransferVO.setOpenId(vo.getOpenId());
        waybillTokenTransferVO.setWaybillId(tradeOrderDO.getLogisticsNo());
        waybillTokenTransferVO.setReceiverPhone(tradeOrderDO.getReceiverMobile());
        waybillTokenTransferVO.setTransId(payOrder.getNo());
        waybillTokenTransferVO.setOrderDetailPath("/pages/order_details/index?order_id=" + vo.getOrderId());
        if(!CollectionUtils.isEmpty(tradeOrderItemDOS)){
            WaybillTokenTransferVO.GoodsInfoDTO dto = new WaybillTokenTransferVO.GoodsInfoDTO();
            dto.setDetailList(
                    tradeOrderItemDOS.stream().map(t->{
                        WaybillTokenTransferVO.GoodsInfoDTO.DetailListDTO detailListDTO
                                 =  new WaybillTokenTransferVO.GoodsInfoDTO.DetailListDTO();
                        detailListDTO.setGoodsName(t.getSpuName());
                        detailListDTO.setGoodsImgUrl(t.getPicUrl());
                        return detailListDTO;
                    }).collect(Collectors.toList())
            );
            waybillTokenTransferVO.setGoodsInfo(dto);
        }

        log.info("params {}",JSON.toJSONString(waybillTokenTransferVO));
        // 此接口微信有问题，返回的是json,但是media type是 text/plain,导致objectmapper转换报错
        // 规避下先，懒得改了
        restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
        WaybillTokenRespVO response = null;
        String url = "https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/trace_waybill?access_token=" + accessToken;
        try {
            response = restTemplate.postForObject(
                    url,
                    new HttpEntity<>(objectMapper.writeValueAsString(waybillTokenTransferVO), headers),
                    WaybillTokenRespVO.class);
        } catch (JsonProcessingException e) {
//            throw new RuntimeException(e);
            log.error(e.getMessage());
        }


        log.info("response:{}" , JSON.toJSONString(response));

        if (response.getErrcode() == 0) {
            return response.getWaybillToken();
        }
        return "";
    }


    private Map<String, List<ShippingDTO>> shippingConvert(List<ShippingDTO> list) {
        Map<String, List<ShippingDTO>> result = new HashMap<>();
        String itemDescs = list.stream().map(p -> p.getItem_desc()).collect(Collectors.joining(";"));
        if (itemDescs.length() > 120) {
            itemDescs = itemDescs.substring(0, 115) + "...";
        }
        ShippingDTO shippingDTO = new ShippingDTO();
        shippingDTO.setTracking_no("");
        shippingDTO.setExpress_company("");
        shippingDTO.setItem_desc(itemDescs);
        List<ShippingDTO> tcpsList = new ArrayList<>();
        tcpsList.add(shippingDTO);
        //同城配送
        result.put("tcps", tcpsList);
        String tcpsGoods = list.stream().filter(p -> p.getExpress_company().equals("TCPS")).map(s -> s.getItem_desc()).collect(Collectors.joining(";"));
        //快递配送
        List<ShippingDTO> kdfhList = list.stream().filter(p -> !p.getExpress_company().equals("TCPS")).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(kdfhList) && StringUtils.isNotBlank(tcpsGoods)) {
            String str = kdfhList.getLast().getItem_desc() + ";其他商品同城发货:" + tcpsGoods;
            if (str.length() > 120) {
                str = str.substring(0, 115) + "...";
            }
            kdfhList.getLast().setItem_desc(str);
        }
        result.put("kdfh", kdfhList);
        return result;
    }


}
